package edu.gmu.dglidde2.ece612.scheduler;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import org.junit.Before;
import org.junit.Test;

/**
 * Unit test suite for the successful scheduling of tasks.
 * 
 * @author Doug Glidden
 */
public class ScheduleTasks {

	private SchedulerImpl scheduler;

	/**
	 * Creates a {@link SchedulerImpl} to be used in the test.
	 * 
	 * @see Before
	 */
	@Before
	public void createScheduler() {
		scheduler = new SchedulerImpl();
	}

	/**
	 * Tests the successful scheduling of a single task on a single processor.
	 */
	@Test
	public void testSingleTask() throws MissingDataException,
			NoFeasibleScheduleException {
		String processorName = "P0";
		scheduler.addProcessor(processorName);

		Task task = new Task();
		task.setName("test-task");
		task.setPhase(0);
		task.setPeriod(10);
		task.setExecutionTime(1);
		task.setRelativeDeadline(10);
		scheduler.addTask(task);

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(10, schedule.getPeriod());
		assertEquals(1, schedule.getScheduleTable().size());
		Schedule.Entry entry = schedule.getScheduleTable().get(0);
		assertEquals(task.getPhase(), entry.getStartTime());

		Task entryTask = entry.getTask();
		assertEquals(task.getName(), entryTask.getName());
		assertEquals(task.getPhase(), entryTask.getPhase());
		assertEquals(task.getPeriod(), entryTask.getPeriod());
		assertEquals(task.getExecutionTime(), entryTask.getExecutionTime());
		assertEquals(task.getRelativeDeadline(),
				entryTask.getRelativeDeadline());
		assertEquals(task.getProcessorNames(), entryTask.getProcessorNames());

		assertEquals(processorName, entry.getProcessorName());
	}

	/**
	 * Tests the successful scheduling of multiple tasks on a single processor.
	 */
	@Test
	public void testMultipleTasks() throws MissingDataException,
			NoFeasibleScheduleException {
		String processorName = "P0";
		scheduler.addProcessor(processorName);

		Map<String, Task> tasks = new HashMap<>(3);
		Task newTask = new Task();
		newTask.setName("task0");
		newTask.setPhase(2);
		newTask.setPeriod(5);
		newTask.setExecutionTime(2);
		newTask.setRelativeDeadline(4);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task1");
		newTask.setPhase(3);
		newTask.setPeriod(10);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(4);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task2");
		newTask.setPhase(5);
		newTask.setPeriod(10);
		newTask.setExecutionTime(2);
		newTask.setRelativeDeadline(5);
		tasks.put(newTask.getName(), newTask);

		for (Task task : tasks.values()) {
			scheduler.addTask(task);
		}

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(10, schedule.getPeriod());
		assertEquals(4, schedule.getScheduleTable().size());
		for (Schedule.Entry entry : schedule.getScheduleTable()) {
			Task entryTask = entry.getTask();
			assertTrue(tasks.containsKey(entryTask.getName()));
			assertTrue(entry.getStartTime() + entryTask.getExecutionTime() <= entryTask
					.getPhase() + entryTask.getRelativeDeadline());

			Task matchingTask = tasks.get(entryTask.getName());
			assertEquals(matchingTask.getPhase(), entryTask.getPhase()
					% matchingTask.getPeriod());
			assertEquals(10, entryTask.getPeriod());
			assertEquals(matchingTask.getExecutionTime(),
					entryTask.getExecutionTime());
			assertEquals(matchingTask.getRelativeDeadline(),
					entryTask.getRelativeDeadline());
			assertEquals(matchingTask.getProcessorNames(),
					entryTask.getProcessorNames());

			assertEquals(processorName, entry.getProcessorName());
		}
	}

	/**
	 * Tests the successful scheduling of multiple tasks that cannot be
	 * scheduled using purely an EDF algorithm on a single processor.
	 */
	@Test
	public void testMultipleTasksEDFNotFeasible() throws MissingDataException,
			NoFeasibleScheduleException {
		String processorName = "P0";
		scheduler.addProcessor(processorName);

		Map<String, Task> tasks = new HashMap<>(3);
		Task newTask = new Task();
		newTask.setName("task0");
		newTask.setPhase(0);
		newTask.setPeriod(5);
		newTask.setExecutionTime(2);
		newTask.setRelativeDeadline(6);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task1");
		newTask.setPhase(1);
		newTask.setPeriod(10);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(3);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task2");
		newTask.setPhase(5);
		newTask.setPeriod(10);
		newTask.setExecutionTime(2);
		newTask.setRelativeDeadline(5);
		tasks.put(newTask.getName(), newTask);

		for (Task task : tasks.values()) {
			scheduler.addTask(task);
		}

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(10, schedule.getPeriod());
		assertEquals(4, schedule.getScheduleTable().size());
		for (Schedule.Entry entry : schedule.getScheduleTable()) {
			Task entryTask = entry.getTask();
			assertTrue(tasks.containsKey(entryTask.getName()));
			assertTrue(entry.getStartTime() + entryTask.getExecutionTime() <= entryTask
					.getPhase() + entryTask.getRelativeDeadline());

			Task matchingTask = tasks.get(entryTask.getName());
			assertEquals(matchingTask.getPhase(), entryTask.getPhase()
					% matchingTask.getPeriod());
			assertEquals(10, entryTask.getPeriod());
			assertEquals(matchingTask.getExecutionTime(),
					entryTask.getExecutionTime());
			assertEquals(matchingTask.getRelativeDeadline(),
					entryTask.getRelativeDeadline());
			assertEquals(matchingTask.getProcessorNames(),
					entryTask.getProcessorNames());

			assertEquals(processorName, entry.getProcessorName());
		}
	}

	/**
	 * Tests the successful scheduling of multiple tasks on multiple processors.
	 */
	@Test
	public void testMultipleProcessors() throws MissingDataException,
			NoFeasibleScheduleException {
		String[] processorNames = { "P0", "P1" };
		for (String processorName : processorNames) {
			scheduler.addProcessor(processorName);
		}

		Map<String, Task> tasks = new HashMap<>(3);
		Task newTask = new Task();
		newTask.setName("task0");
		newTask.setPhase(2);
		newTask.setPeriod(5);
		newTask.setExecutionTime(4);
		newTask.setRelativeDeadline(4);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task1");
		newTask.setPhase(3);
		newTask.setPeriod(10);
		newTask.setExecutionTime(6);
		newTask.setRelativeDeadline(8);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task2");
		newTask.setPhase(5);
		newTask.setPeriod(10);
		newTask.setExecutionTime(4);
		newTask.setRelativeDeadline(10);
		tasks.put(newTask.getName(), newTask);

		for (Task task : tasks.values()) {
			scheduler.addTask(task);
		}

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(10, schedule.getPeriod());
		assertEquals(4, schedule.getScheduleTable().size());
		for (Schedule.Entry entry : schedule.getScheduleTable()) {
			Task entryTask = entry.getTask();
			assertTrue(tasks.containsKey(entryTask.getName()));
			assertTrue(entry.getStartTime() + entryTask.getExecutionTime() <= entryTask
					.getPhase() + entryTask.getRelativeDeadline());

			Task matchingTask = tasks.get(entryTask.getName());
			assertEquals(matchingTask.getPhase(), entryTask.getPhase()
					% matchingTask.getPeriod());
			assertEquals(10, entryTask.getPeriod());
			assertEquals(matchingTask.getExecutionTime(),
					entryTask.getExecutionTime());
			assertEquals(matchingTask.getRelativeDeadline(),
					entryTask.getRelativeDeadline());
			assertEquals(matchingTask.getProcessorNames(),
					entryTask.getProcessorNames());

			assertTrue(Arrays.asList(processorNames).contains(
					entry.getProcessorName()));
		}
	}

	/**
	 * Tests the successful scheduling of multiple tasks that cannot be
	 * scheduled using purely an EDF algorithm on multiple processors.
	 */
	@Test
	public void testMultipleProcessorsEDFNotFeasible()
			throws MissingDataException, NoFeasibleScheduleException {
		String[] processorNames = { "P0", "P1" };
		for (String processorName : processorNames) {
			scheduler.addProcessor(processorName);
		}

		Map<String, Task> tasks = new HashMap<>(3);
		Task newTask = new Task();
		newTask.setName("task0");
		newTask.setPhase(2);
		newTask.setPeriod(5);
		newTask.setExecutionTime(4);
		newTask.setRelativeDeadline(6);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task1");
		newTask.setPhase(3);
		newTask.setPeriod(10);
		newTask.setExecutionTime(6);
		newTask.setRelativeDeadline(8);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task2");
		newTask.setPhase(5);
		newTask.setPeriod(10);
		newTask.setExecutionTime(4);
		newTask.setRelativeDeadline(10);
		tasks.put(newTask.getName(), newTask);

		for (Task task : tasks.values()) {
			scheduler.addTask(task);
		}

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(10, schedule.getPeriod());
		assertEquals(4, schedule.getScheduleTable().size());
		for (Schedule.Entry entry : schedule.getScheduleTable()) {
			Task entryTask = entry.getTask();
			assertTrue(tasks.containsKey(entryTask.getName()));
			assertTrue(entry.getStartTime() + entryTask.getExecutionTime() <= entryTask
					.getPhase() + entryTask.getRelativeDeadline());

			Task matchingTask = tasks.get(entryTask.getName());
			assertEquals(matchingTask.getPhase(), entryTask.getPhase()
					% matchingTask.getPeriod());
			assertEquals(10, entryTask.getPeriod());
			assertEquals(matchingTask.getExecutionTime(),
					entryTask.getExecutionTime());
			assertEquals(matchingTask.getRelativeDeadline(),
					entryTask.getRelativeDeadline());
			assertEquals(matchingTask.getProcessorNames(),
					entryTask.getProcessorNames());

			assertTrue(Arrays.asList(processorNames).contains(
					entry.getProcessorName()));
		}
	}

	/**
	 * Tests the successful scheduling of multiple tasks, all of which are bound
	 * to a single processor, with additional processors available.
	 */
	@Test
	public void testAllTasksBoundToSameSingleProcessor()
			throws MissingDataException, NoFeasibleScheduleException {
		String[] processorNames = { "P0", "P1" };
		for (String processorName : processorNames) {
			scheduler.addProcessor(processorName);
		}

		Map<String, Task> tasks = new HashMap<>(3);
		Task newTask = new Task();
		newTask.setName("task0");
		newTask.setPhase(2);
		newTask.setPeriod(5);
		newTask.setExecutionTime(2);
		newTask.setRelativeDeadline(4);
		newTask.setProcessorNames(new HashSet<>(Arrays
				.asList(processorNames[0])));
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task1");
		newTask.setPhase(3);
		newTask.setPeriod(10);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(4);
		newTask.setProcessorNames(new HashSet<>(Arrays
				.asList(processorNames[0])));
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task2");
		newTask.setPhase(5);
		newTask.setPeriod(10);
		newTask.setExecutionTime(2);
		newTask.setRelativeDeadline(5);
		newTask.setProcessorNames(new HashSet<>(Arrays
				.asList(processorNames[0])));
		tasks.put(newTask.getName(), newTask);

		for (Task task : tasks.values()) {
			scheduler.addTask(task);
		}

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(10, schedule.getPeriod());
		assertEquals(4, schedule.getScheduleTable().size());
		for (Schedule.Entry entry : schedule.getScheduleTable()) {
			Task entryTask = entry.getTask();
			assertTrue(tasks.containsKey(entryTask.getName()));
			assertTrue(entry.getStartTime() + entryTask.getExecutionTime() <= entryTask
					.getPhase() + entryTask.getRelativeDeadline());

			Task matchingTask = tasks.get(entryTask.getName());
			assertEquals(matchingTask.getPhase(), entryTask.getPhase()
					% matchingTask.getPeriod());
			assertEquals(10, entryTask.getPeriod());
			assertEquals(matchingTask.getExecutionTime(),
					entryTask.getExecutionTime());
			assertEquals(matchingTask.getRelativeDeadline(),
					entryTask.getRelativeDeadline());
			assertEquals(matchingTask.getProcessorNames(),
					entryTask.getProcessorNames());

			assertTrue(entryTask.getProcessorNames().contains(
					entry.getProcessorName()));
		}
	}

	/**
	 * Tests the successful scheduling of multiple tasks, some of which are
	 * bound to a single processor, with additional processors available.
	 */
	@Test
	public void testSomeTasksBoundToSameSingleProcessor()
			throws MissingDataException, NoFeasibleScheduleException {
		String[] processorNames = { "P0", "P1" };
		for (String processorName : processorNames) {
			scheduler.addProcessor(processorName);
		}

		Map<String, Task> tasks = new HashMap<>(5);
		Task newTask = new Task();
		newTask.setName("unbound-task0");
		newTask.setPhase(0);
		newTask.setPeriod(12);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(12);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("unbound-task1");
		newTask.setPhase(0);
		newTask.setPeriod(12);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(12);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("unbound-task2");
		newTask.setPhase(0);
		newTask.setPeriod(12);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(12);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("unbound-task3");
		newTask.setPhase(0);
		newTask.setPeriod(12);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(12);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("bound-task0");
		newTask.setPhase(1);
		newTask.setPeriod(6);
		newTask.setExecutionTime(4);
		newTask.setRelativeDeadline(5);
		newTask.setProcessorNames(new HashSet<>(Arrays
				.asList(processorNames[0])));
		tasks.put(newTask.getName(), newTask);

		for (Task task : tasks.values()) {
			scheduler.addTask(task);
		}

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(12, schedule.getPeriod());
		assertEquals(6, schedule.getScheduleTable().size());
		for (Schedule.Entry entry : schedule.getScheduleTable()) {
			Task entryTask = entry.getTask();
			assertTrue(tasks.containsKey(entryTask.getName()));
			assertTrue(entry.getStartTime() + entryTask.getExecutionTime() <= entryTask
					.getPhase() + entryTask.getRelativeDeadline());

			Task matchingTask = tasks.get(entryTask.getName());
			assertEquals(matchingTask.getPhase(), entryTask.getPhase()
					% matchingTask.getPeriod());
			assertEquals(12, entryTask.getPeriod());
			assertEquals(matchingTask.getExecutionTime(),
					entryTask.getExecutionTime());
			assertEquals(matchingTask.getRelativeDeadline(),
					entryTask.getRelativeDeadline());
			assertEquals(matchingTask.getProcessorNames(),
					entryTask.getProcessorNames());

			if (entryTask.getProcessorNames() != null) {
				assertTrue(entryTask.getProcessorNames().contains(
						entry.getProcessorName()));
			} else {
				assertTrue(Arrays.asList(processorNames).contains(
						entry.getProcessorName()));
			}
		}
	}

	/**
	 * Tests the successful scheduling of multiple tasks, all of which are bound
	 * to various individual processors.
	 */
	@Test
	public void testAllTasksBoundToDifferentSingleProcessor()
			throws MissingDataException, NoFeasibleScheduleException {
		String[] processorNames = { "P0", "P1" };
		for (String processorName : processorNames) {
			scheduler.addProcessor(processorName);
		}

		Map<String, Task> tasks = new HashMap<>(3);
		Task newTask = new Task();
		newTask.setName("task0");
		newTask.setPhase(2);
		newTask.setPeriod(5);
		newTask.setExecutionTime(4);
		newTask.setRelativeDeadline(4);
		newTask.setProcessorNames(new HashSet<>(Arrays
				.asList(processorNames[0])));
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task1");
		newTask.setPhase(3);
		newTask.setPeriod(10);
		newTask.setExecutionTime(6);
		newTask.setRelativeDeadline(8);
		newTask.setProcessorNames(new HashSet<>(Arrays
				.asList(processorNames[1])));
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task2");
		newTask.setPhase(5);
		newTask.setPeriod(10);
		newTask.setExecutionTime(4);
		newTask.setRelativeDeadline(10);
		newTask.setProcessorNames(new HashSet<>(Arrays
				.asList(processorNames[1])));
		tasks.put(newTask.getName(), newTask);

		for (Task task : tasks.values()) {
			scheduler.addTask(task);
		}

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(10, schedule.getPeriod());
		assertEquals(4, schedule.getScheduleTable().size());
		for (Schedule.Entry entry : schedule.getScheduleTable()) {
			Task entryTask = entry.getTask();
			assertTrue(tasks.containsKey(entryTask.getName()));
			assertTrue(entry.getStartTime() + entryTask.getExecutionTime() <= entryTask
					.getPhase() + entryTask.getRelativeDeadline());

			Task matchingTask = tasks.get(entryTask.getName());
			assertEquals(matchingTask.getPhase(), entryTask.getPhase()
					% matchingTask.getPeriod());
			assertEquals(10, entryTask.getPeriod());
			assertEquals(matchingTask.getExecutionTime(),
					entryTask.getExecutionTime());
			assertEquals(matchingTask.getRelativeDeadline(),
					entryTask.getRelativeDeadline());
			assertEquals(matchingTask.getProcessorNames(),
					entryTask.getProcessorNames());

			assertTrue(entryTask.getProcessorNames().contains(
					entry.getProcessorName()));
		}
	}

	/**
	 * Tests the successful scheduling of multiple tasks, some of which are
	 * bound to various individual processors.
	 */
	@Test
	public void testSomeTasksBoundToDifferentSingleProcessor()
			throws MissingDataException, NoFeasibleScheduleException {
		String[] processorNames = { "P0", "P1" };
		for (String processorName : processorNames) {
			scheduler.addProcessor(processorName);
		}

		Map<String, Task> tasks = new HashMap<>(3);
		Task newTask = new Task();
		newTask.setName("unbound-task0");
		newTask.setPhase(0);
		newTask.setPeriod(5);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(10);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("bound-task0");
		newTask.setPhase(1);
		newTask.setPeriod(10);
		newTask.setExecutionTime(6);
		newTask.setRelativeDeadline(6);
		newTask.setProcessorNames(new HashSet<>(Arrays
				.asList(processorNames[0])));
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("bound-task1");
		newTask.setPhase(2);
		newTask.setPeriod(10);
		newTask.setExecutionTime(4);
		newTask.setRelativeDeadline(4);
		newTask.setProcessorNames(new HashSet<>(Arrays
				.asList(processorNames[1])));
		tasks.put(newTask.getName(), newTask);

		for (Task task : tasks.values()) {
			scheduler.addTask(task);
		}

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(10, schedule.getPeriod());
		assertEquals(4, schedule.getScheduleTable().size());
		for (Schedule.Entry entry : schedule.getScheduleTable()) {
			Task entryTask = entry.getTask();
			assertTrue(tasks.containsKey(entryTask.getName()));
			assertTrue(entry.getStartTime() + entryTask.getExecutionTime() <= entryTask
					.getPhase() + entryTask.getRelativeDeadline());

			Task matchingTask = tasks.get(entryTask.getName());
			assertEquals(matchingTask.getPhase(), entryTask.getPhase()
					% matchingTask.getPeriod());
			assertEquals(10, entryTask.getPeriod());
			assertEquals(matchingTask.getExecutionTime(),
					entryTask.getExecutionTime());
			assertEquals(matchingTask.getRelativeDeadline(),
					entryTask.getRelativeDeadline());
			assertEquals(matchingTask.getProcessorNames(),
					entryTask.getProcessorNames());

			if (entryTask.getProcessorNames() != null) {
				assertTrue(entryTask.getProcessorNames().contains(
						entry.getProcessorName()));
			} else {
				assertTrue(Arrays.asList(processorNames).contains(
						entry.getProcessorName()));
			}
		}
	}

	/**
	 * Tests the successful scheduling of multiple tasks, all of which are bound
	 * to a single group of individual processors, with additional processors
	 * available.
	 */
	@Test
	public void testAllTasksBoundToSameMultipleProcessors()
			throws MissingDataException, NoFeasibleScheduleException {
		String[] processorNames = { "P0", "P1", "P2" };
		for (String processorName : processorNames) {
			scheduler.addProcessor(processorName);
		}

		Map<String, Task> tasks = new HashMap<>(3);
		Task newTask = new Task();
		newTask.setName("task0");
		newTask.setPhase(2);
		newTask.setPeriod(5);
		newTask.setExecutionTime(4);
		newTask.setRelativeDeadline(4);
		newTask.setProcessorNames(new HashSet<>(Arrays.asList(
				processorNames[0], processorNames[1])));
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task1");
		newTask.setPhase(3);
		newTask.setPeriod(10);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(4);
		newTask.setProcessorNames(new HashSet<>(Arrays.asList(
				processorNames[0], processorNames[1])));
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task2");
		newTask.setPhase(5);
		newTask.setPeriod(10);
		newTask.setExecutionTime(2);
		newTask.setRelativeDeadline(5);
		newTask.setProcessorNames(new HashSet<>(Arrays.asList(
				processorNames[0], processorNames[1])));
		tasks.put(newTask.getName(), newTask);

		for (Task task : tasks.values()) {
			scheduler.addTask(task);
		}

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(10, schedule.getPeriod());
		assertEquals(4, schedule.getScheduleTable().size());
		for (Schedule.Entry entry : schedule.getScheduleTable()) {
			Task entryTask = entry.getTask();
			assertTrue(tasks.containsKey(entryTask.getName()));
			assertTrue(entry.getStartTime() + entryTask.getExecutionTime() <= entryTask
					.getPhase() + entryTask.getRelativeDeadline());

			Task matchingTask = tasks.get(entryTask.getName());
			assertEquals(matchingTask.getPhase(), entryTask.getPhase()
					% matchingTask.getPeriod());
			assertEquals(10, entryTask.getPeriod());
			assertEquals(matchingTask.getExecutionTime(),
					entryTask.getExecutionTime());
			assertEquals(matchingTask.getRelativeDeadline(),
					entryTask.getRelativeDeadline());
			assertEquals(matchingTask.getProcessorNames(),
					entryTask.getProcessorNames());

			assertTrue(entryTask.getProcessorNames().contains(
					entry.getProcessorName()));
		}
	}

	/**
	 * Tests the successful scheduling of multiple tasks, some of which are
	 * bound to a single group of individual processors, with additional
	 * processors available.
	 */
	@Test
	public void testSomeTasksBoundToSameMultipleProcessors()
			throws MissingDataException, NoFeasibleScheduleException {
		String[] processorNames = { "P0", "P1", "P2" };
		for (String processorName : processorNames) {
			scheduler.addProcessor(processorName);
		}

		Map<String, Task> tasks = new HashMap<>(6);
		Task newTask = new Task();
		newTask.setName("unbound-task0");
		newTask.setPhase(0);
		newTask.setPeriod(12);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(12);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("unbound-task1");
		newTask.setPhase(0);
		newTask.setPeriod(12);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(12);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("unbound-task2");
		newTask.setPhase(0);
		newTask.setPeriod(12);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(12);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("unbound-task3");
		newTask.setPhase(0);
		newTask.setPeriod(12);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(12);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("bound-task0");
		newTask.setPhase(1);
		newTask.setPeriod(12);
		newTask.setExecutionTime(8);
		newTask.setRelativeDeadline(8);
		newTask.setProcessorNames(new HashSet<>(Arrays.asList(
				processorNames[0], processorNames[1])));
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("bound-task1");
		newTask.setPhase(2);
		newTask.setPeriod(12);
		newTask.setExecutionTime(8);
		newTask.setRelativeDeadline(8);
		newTask.setProcessorNames(new HashSet<>(Arrays.asList(
				processorNames[0], processorNames[1])));
		tasks.put(newTask.getName(), newTask);

		for (Task task : tasks.values()) {
			scheduler.addTask(task);
		}

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(12, schedule.getPeriod());
		assertEquals(6, schedule.getScheduleTable().size());
		for (Schedule.Entry entry : schedule.getScheduleTable()) {
			Task entryTask = entry.getTask();
			assertTrue(tasks.containsKey(entryTask.getName()));
			assertTrue(entry.getStartTime() + entryTask.getExecutionTime() <= entryTask
					.getPhase() + entryTask.getRelativeDeadline());

			Task matchingTask = tasks.get(entryTask.getName());
			assertEquals(matchingTask.getPhase(), entryTask.getPhase()
					% matchingTask.getPeriod());
			assertEquals(12, entryTask.getPeriod());
			assertEquals(matchingTask.getExecutionTime(),
					entryTask.getExecutionTime());
			assertEquals(matchingTask.getRelativeDeadline(),
					entryTask.getRelativeDeadline());
			assertEquals(matchingTask.getProcessorNames(),
					entryTask.getProcessorNames());

			if (entryTask.getProcessorNames() != null) {
				assertTrue(entryTask.getProcessorNames().contains(
						entry.getProcessorName()));
			} else {
				assertTrue(Arrays.asList(processorNames).contains(
						entry.getProcessorName()));
			}
		}
	}

	/**
	 * Tests the successful scheduling of multiple tasks, all of which are bound
	 * to a various groups of individual processors.
	 */
	@Test
	public void testAllTasksBoundToDifferentMultipleProcessors()
			throws MissingDataException, NoFeasibleScheduleException {
		String[] processorNames = { "P0", "P1", "P2" };
		for (String processorName : processorNames) {
			scheduler.addProcessor(processorName);
		}

		Map<String, Task> tasks = new HashMap<>(3);
		Task newTask = new Task();
		newTask.setName("task0");
		newTask.setPhase(2);
		newTask.setPeriod(5);
		newTask.setExecutionTime(4);
		newTask.setRelativeDeadline(4);
		newTask.setProcessorNames(new HashSet<>(Arrays.asList(
				processorNames[0], processorNames[1])));
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task1");
		newTask.setPhase(3);
		newTask.setPeriod(10);
		newTask.setExecutionTime(6);
		newTask.setRelativeDeadline(8);
		newTask.setProcessorNames(new HashSet<>(Arrays.asList(
				processorNames[1], processorNames[2])));
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("task2");
		newTask.setPhase(5);
		newTask.setPeriod(10);
		newTask.setExecutionTime(4);
		newTask.setRelativeDeadline(10);
		newTask.setProcessorNames(new HashSet<>(Arrays.asList(
				processorNames[1], processorNames[2])));
		tasks.put(newTask.getName(), newTask);

		for (Task task : tasks.values()) {
			scheduler.addTask(task);
		}

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(10, schedule.getPeriod());
		assertEquals(4, schedule.getScheduleTable().size());
		for (Schedule.Entry entry : schedule.getScheduleTable()) {
			Task entryTask = entry.getTask();
			assertTrue(tasks.containsKey(entryTask.getName()));
			assertTrue(entry.getStartTime() + entryTask.getExecutionTime() <= entryTask
					.getPhase() + entryTask.getRelativeDeadline());

			Task matchingTask = tasks.get(entryTask.getName());
			assertEquals(matchingTask.getPhase(), entryTask.getPhase()
					% matchingTask.getPeriod());
			assertEquals(10, entryTask.getPeriod());
			assertEquals(matchingTask.getExecutionTime(),
					entryTask.getExecutionTime());
			assertEquals(matchingTask.getRelativeDeadline(),
					entryTask.getRelativeDeadline());
			assertEquals(matchingTask.getProcessorNames(),
					entryTask.getProcessorNames());

			assertTrue(entryTask.getProcessorNames().contains(
					entry.getProcessorName()));
		}
	}

	/**
	 * Tests the successful scheduling of multiple tasks, some of which are
	 * bound to a various groups of individual processors.
	 */
	@Test
	public void testSomeTasksBoundToDifferentMultipleProcessors()
			throws MissingDataException, NoFeasibleScheduleException {
		String[] processorNames = { "P0", "P1", "P2" };
		for (String processorName : processorNames) {
			scheduler.addProcessor(processorName);
		}

		Map<String, Task> tasks = new HashMap<>(3);
		Task newTask = new Task();
		newTask.setName("unbound-task0");
		newTask.setPhase(0);
		newTask.setPeriod(5);
		newTask.setExecutionTime(3);
		newTask.setRelativeDeadline(5);
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("bound-task0");
		newTask.setPhase(1);
		newTask.setPeriod(10);
		newTask.setExecutionTime(6);
		newTask.setRelativeDeadline(6);
		newTask.setProcessorNames(new HashSet<>(Arrays.asList(
				processorNames[0], processorNames[1])));
		tasks.put(newTask.getName(), newTask);

		newTask = new Task();
		newTask.setName("bound-task1");
		newTask.setPhase(2);
		newTask.setPeriod(10);
		newTask.setExecutionTime(4);
		newTask.setRelativeDeadline(4);
		newTask.setProcessorNames(new HashSet<>(Arrays.asList(
				processorNames[1], processorNames[2])));
		tasks.put(newTask.getName(), newTask);

		for (Task task : tasks.values()) {
			scheduler.addTask(task);
		}

		Schedule schedule = scheduler.generateSchedule();
		assertEquals(10, schedule.getPeriod());
		assertEquals(4, schedule.getScheduleTable().size());
		for (Schedule.Entry entry : schedule.getScheduleTable()) {
			Task entryTask = entry.getTask();
			assertTrue(tasks.containsKey(entryTask.getName()));
			assertTrue(entry.getStartTime() + entryTask.getExecutionTime() <= entryTask
					.getPhase() + entryTask.getRelativeDeadline());

			Task matchingTask = tasks.get(entryTask.getName());
			assertEquals(matchingTask.getPhase(), entryTask.getPhase()
					% matchingTask.getPeriod());
			assertEquals(10, entryTask.getPeriod());
			assertEquals(matchingTask.getExecutionTime(),
					entryTask.getExecutionTime());
			assertEquals(matchingTask.getRelativeDeadline(),
					entryTask.getRelativeDeadline());
			assertEquals(matchingTask.getProcessorNames(),
					entryTask.getProcessorNames());

			if (entryTask.getProcessorNames() != null) {
				assertTrue(entryTask.getProcessorNames().contains(
						entry.getProcessorName()));
			} else {
				assertTrue(Arrays.asList(processorNames).contains(
						entry.getProcessorName()));
			}
		}
	}

}
